package com.huawei.codecheck.uccp.plugin.utils.http;

import com.google.gson.Gson;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.AuthCache;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.ServiceUnavailableRetryStrategy;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * http 连接客户端
 *
 * @since 2019-09-23
 */
public class HttpClient {
    private static final String ACCESS_TOKEN_KEY = "x-auth-token";

    private CloseableHttpClient client = null;

    private CloseableHttpClient httpClient = null;

    /**
     * 初始化
     */
    public static class HttpClientHolder {
        private static final HttpClient INSTANCE = new HttpClient();
    }

    /**
     * 获取单例实例
     *
     * @return HttpClient
     */
    public static final HttpClient getInstance() {
        return HttpClientHolder.INSTANCE;
    }

    private HttpClient() {
        System.setProperty("http.keepAlive", "false");
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        HttpRequestRetryHandler retryHandler = new ExceptionHttpRetryHandler();
        ServiceUnavailableRetryStrategy retryStrategy = new HttpServiceRetryStrategy();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10_000)
                .setConnectTimeout(10_000)
                .setSocketTimeout(60_000)
                .build();
        cm.setMaxTotal(800);
        cm.setDefaultMaxPerRoute(800);
        client = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setMaxConnPerRoute(800)
                .setMaxConnTotal(800)
                .setConnectionManager(cm)
                .setRetryHandler(retryHandler)
                .setServiceUnavailableRetryStrategy(retryStrategy)
                .build();
        try {
            SSLConnectionSocketFactory sslsf = createSSLConnectionSocketFactory();
            httpClient = HttpClients.custom()
                    .setMaxConnPerRoute(800)
                    .setMaxConnTotal(800)
                    .setSSLSocketFactory(sslsf)
                    .setRetryHandler(retryHandler)
                    .setServiceUnavailableRetryStrategy(retryStrategy)
                    .setDefaultRequestConfig(requestConfig)
                    .useSystemProperties()
                    .build();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    private CloseableHttpClient loadHttpClient(String url) {
        if (url != null && url.startsWith("http:")) {
            return client;
        } else {
            return httpClient;
        }
    }




    /**
     * get方式调用
     *
     * @param url 请求地址
     * @return 返回的结果
     */
    public String get(String url) {
        return get(url, null);
    }

    /**
     * get方式调用，没有apiGateWayKey信息的话就普通调用
     *
     * @param url 请求地址
     * @param headers 请求头
     * @return 返回的结果
     */
    public String get(String url, Map<String, String> headers) {
        if (null == url || "".equals(url)) {
            throw new RuntimeException("url is null");
        }
        URI uri = URI.create(url);
        HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);
        CloseableHttpClient closeableHttpClient = loadHttpClient(url);
        HttpRequestBase httpGet = new HttpGet(uri);
        updateHeader(httpGet, headers);
        HttpClientContext localContext = HttpClientContext.create();
        localContext.setAuthCache(authCache);
        HttpResponse response = null;
        String result = "";
        try {
            response = closeableHttpClient.execute(host, httpGet, localContext);
            result = handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e.getClass().getName() + e.getMessage());
        } finally {
            httpGet.releaseConnection();
        }
        return result;
    }

    /**
     * post请求
     *
     * @param url 请求地址
     * @param objectRequest 请求的参数
     * @return String
     */
    public String post(String url, Object objectRequest) {
        return post(url, objectRequest, null);
    }



    /**
     * post请求
     *
     * @param url 请求地址
     * @param objectRequest 请求的参数
     * @param headers headers
     * @return String
     */
    public String post(String url, Object objectRequest, Map<String, String> headers) {
        URI uri = URI.create(url);
        HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);
        HttpRequestBase httpRequestBase = new HttpPost();
        // new apigateway httpPost

        // before httpPost
        HttpPost httpPost = new HttpPost(uri);
        StringEntity stringEntity = null;
        if (objectRequest instanceof String) {
            stringEntity = new StringEntity((String) objectRequest, HTTP.UTF_8);
        } else {
            stringEntity = new StringEntity(new Gson().toJson(objectRequest), HTTP.UTF_8);
        }
        httpPost.setEntity(stringEntity);
        httpRequestBase = httpPost;
        updateHeader(httpRequestBase, headers);
        String result = "";
        try {
            HttpClientContext localContext = HttpClientContext.create();
            localContext.setAuthCache(authCache);
            CloseableHttpClient closeableHttpClient = loadHttpClient(url);
            HttpResponse response = closeableHttpClient.execute(host, httpRequestBase, localContext);
            result = handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            httpRequestBase.releaseConnection();
        }
        return result;
    }

    private static String handleResponse(HttpResponse response) throws IOException {
        String result;
        int code = response.getStatusLine().getStatusCode();
        if (response.getEntity() == null) {
            result = "";
        } else {
            result = EntityUtils.toString(response.getEntity());
        }
        if (code < 200 || code > 300) {
            throw new RuntimeException("HTTP ERROR, Code: " + code + ", Result: " + result);
        }
        return result;
    }

    private void updateHeader(HttpRequestBase httpBase, Map<String, String> headers) {
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpBase.setHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    private static SSLConnectionSocketFactory createSSLConnectionSocketFactory()
            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }

            @Override
            public void verify(String host, SSLSocket ssl) {
            }

            @Override
            public void verify(String host, X509Certificate cert) {
            }

            @Override
            public void verify(String host, String[] cns, String[] subjectAlts) {
            }

        });
        return sslsf;
    }

}
